﻿#region [ Code Modification History ]
//*******************************************************************************************************
//  GZipTool.cs 
//
//  Code Modification History:
//  -----------------------------------------------------------------------------------------------------
//  15/01/2017 - V. Dennis Kholin
//       Generated original version of source code. 
//*******************************************************************************************************
#endregion 

#region [ Using ]
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using GZipStreamTool.Base.Core;
using GZipStreamTool.Base.Gzip;
#endregion

namespace GZipStreamTool.Base.GZipTools
{
    #region [ Enums ]
    /// <summary>
    /// MemoryConsumption
    /// </summary>
    public enum MemoryConsumption
    {
        Low,
        Hight
    }
    #endregion 


    /// <summary>
    /// ByteBlockTool
    /// </summary>
    public class GZipTool : IDisposable
    {

        #region [ Events ]
        /// <summary>
        /// FileReadOutBegins
        /// </summary>
        public event ByteBlockWriterDelegate FileReadOutBegins;
        /// <summary>
        /// FileReadOutCompleted
        /// </summary>
        public event ByteBlockWriterDelegate FileReadOutCompleted;
        /// <summary>
        /// FileActionBegins
        /// </summary>
        public event ByteBlockWriterDelegate FileActionBegins;
        /// <summary>
        /// FileActionCompleted
        /// </summary>
        public event ByteBlockWriterDelegate FileActionCompleted;
        /// <summary>
        /// FileSavingBegins
        /// </summary>
        public event ByteBlockWriterDelegate FileSavingBegins;
        /// <summary>
        /// FileSavingCompleted
        /// </summary>
        public event ByteBlockWriterDelegate FileSavingCompleted;
        #endregion

        #region [ Event Rasing ]
        /// <summary>
        /// OnFileReadOutBegins
        /// </summary>
        public void OnFileReadOutBegins()
        {
            if (FileReadOutBegins != null)
            {
                FileReadOutBegins?.Invoke(new EventArgs(), this);
            }
        }
        /// <summary>
        /// OnFileReadOutCompleted
        /// </summary>
        public void OnFileReadOutCompleted()
        {
            if (FileReadOutCompleted != null)
            {
                FileReadOutCompleted?.Invoke(new EventArgs(), this);
            }
        }
        /// <summary>
        /// OnFileActionCompleted
        /// </summary>
        public void OnFileActionBegins()
        {
            if (FileActionBegins != null)
            {
                FileActionBegins?.Invoke(new EventArgs(), this);
            }
        }
        /// <summary>
        /// OnFileActionCompleted
        /// </summary>
        public void OnFileActionCompleted()
        {
            if (FileActionCompleted != null)
            {
                FileActionCompleted?.Invoke(new EventArgs(), this);
            }
        }
        /// <summary>
        /// OnFileSavingCompleted
        /// </summary>
        public void OnFileSavingBegins()
        {
            if (FileSavingBegins != null)
            {
                FileSavingBegins?.Invoke(new EventArgs(), this);
            }
        }
        /// <summary>
        /// OnFileSavingCompleted
        /// </summary>
        public void OnFileSavingCompleted()
        {
            if (FileSavingCompleted != null)
            {
                FileSavingCompleted?.Invoke(new EventArgs(), this);
            }
        }
        #endregion 

        #region [ Members ]
        private ByteBlockReader _bbr;
        private ByteBlockWriter _bbw;
        private List<ByteBlock> _bbc;
        private List<GZipBlock> _bbd;
        private ActionType _action;
        private MemoryConsumption _mc;
        private string _file;
        #endregion

        #region [ Properties ]
        /// <summary>
        /// MemConsumption
        /// </summary>
        public MemoryConsumption MemConsumption
        {
            get { return this._mc; }
            set { this._mc = value; }
        }
        /// <summary>
        /// Reader
        /// </summary>
        public ByteBlockReader Reader
        {
            get { return this._bbr; }
            set { this._bbr = value; }
        }
        /// <summary>
        /// Writer
        /// </summary>
        public ByteBlockWriter Writer
        {
            get { return this._bbw; }
            set { this._bbw = value; }
        }
        /// <summary>
        /// Action
        /// </summary>
        public ActionType Action
        {
            get { return this._action; }
            set { this._action = value; }
        }
        #endregion

        #region [ Constructors ]
        /// <summary>
        /// ByteBlockTool
        /// </summary>
        public GZipTool(string file)
        {
            this._file = file;
        }
        #endregion

        #region [ Methods ]
        /// <summary>
        /// Compress
        /// </summary>
        public void Compress()
        {
            _bbr = new ByteBlockReader();
            _bbw = new ByteBlockWriter();

            string reader = this._file;
            string writer = this._file = this._file + ".gz";

            _bbr.ReaderFileStream = new FileStream(reader, FileMode.Open, FileAccess.Read, FileShare.Read);
            _bbw.WriterFileStream = new FileStream(writer, FileMode.Create, FileAccess.Write, FileShare.Write);

            Write();
        }

        /// <summary>
        /// Decompress
        /// </summary>
        public void Decompress()
        {
            _bbr = new ByteBlockReader();
            _bbw = new ByteBlockWriter();

            string reader = this._file;
            string writer = this._file.Replace(".gz", "");

            _bbr.ReaderFileStream = new FileStream(reader, FileMode.Open, FileAccess.Read, FileShare.Read);
            _bbw.WriterFileStream = new FileStream(writer, FileMode.Create, FileAccess.Write, FileShare.Write);

            Write();
        }

        /// <summary>
        /// Write
        /// </summary>
        public void Write()
        {
            #region [ Container ]
            this._bbc = new List<ByteBlock>();
            this._bbd = new List<GZipBlock>();
            #endregion

            


            switch (_action)
            { 

                case ActionType.Compress:

                    switch (MemConsumption)
                    {
                        case MemoryConsumption.Low:

                            #region [ Combined ]
                            int compressioncounter = 0;
                            int compressioncapacity = 128;

                            OnFileReadOutBegins();
                            OnFileActionBegins();
                            OnFileSavingBegins();

                            foreach (ByteBlock b in ByteBlockReader.Read(_bbr.ReaderFileStream))
                            {
                                _bbc.Add(b);
                                if (compressioncounter > compressioncapacity)
                                {
                                    _bbc.AsParallel()
                                        .ForAll((bb) => { bb.BlockOut = GZipTools.GZipStreamTool.Compress(bb.Block); });

                                    // OnFileSavingBegins();

                                    foreach (ByteBlock bi in _bbc)
                                    {
                                        _bbw.Write(bi.BlockOut);
                                    }

                                    //  OnFileSavingCompleted();

                                    _bbc.Clear();
                                    compressioncounter = 0;
                                }
                                compressioncounter = compressioncounter + 1;
                            }

                            if (compressioncounter > 0)
                            {
                                _bbc.AsParallel().ForAll((bb) => { bb.BlockOut = GZipTools.GZipStreamTool.Compress(bb.Block); });

                                foreach (ByteBlock bi in _bbc)
                                {
                                    _bbw.Write(bi.BlockOut);
                                }

                            }
                            _bbc.Clear();

                            OnFileActionCompleted();
                            OnFileReadOutCompleted();
                            OnFileSavingCompleted();
                            #endregion

                            break;

                        default:

                            #region [ Read ]
                            OnFileReadOutBegins();
                            foreach (ByteBlock b in ByteBlockReader.Read(_bbr.ReaderFileStream))
                            {
                                _bbc.Add(b);
                            }
                            OnFileReadOutCompleted();
                            #endregion

                            #region [ Action ] 
                            OnFileActionBegins();
                            _bbc.AsParallel().ForAll((bb) => { bb.BlockOut = GZipTools.GZipStreamTool.Compress(bb.Block); });
                            OnFileActionCompleted();
                            #endregion

                            #region [ Writeout ]
                            OnFileSavingBegins();
                            using (_bbw.WriterFileStream)
                            {
                                foreach (ByteBlock b in _bbc)
                                {
                                    _bbw.Write(b.BlockOut);
                                }

                            }
                            _bbc.Clear();
                            OnFileSavingCompleted();
                            #endregion

                            break;

                    } 
                    break;

                case ActionType.Decompress:

                    GZipBlockHeader gzbh = new GZipBlockHeader();
                    gzbh.InitHeader(_bbr.ReaderFileStream);

                    switch (MemConsumption)
                    {
                        case MemoryConsumption.Low:

                            #region [ Combined ]

                            int decompressioncounter = 0;
                            int decompressioncapacity = 128;

                            OnFileReadOutBegins();
                            OnFileActionBegins();
                            OnFileSavingBegins();

                            foreach (GZipBlock item in ByteBlockReader.ReagGZipBlock(_bbr.ReaderFileStream, gzbh))
                            {
                                _bbd.Add(item);
                                if (decompressioncounter > decompressioncapacity)
                                {
                                    /*
                                   for (int j = 0; j < _bbd.Count; j++)
                                   {
                                       GZipBlock bb = _bbd.ElementAt(j);
                                       try
                                       {
                                           bb.BlockOut = GZipTools.GZipStreamTool.Decompress(bb.Block);
                                       }
                                       catch (Exception e)
                                       {
                                           Console.WriteLine(e.Message);
                                       }
                                   } */

                                    _bbd.AsParallel()
                                       .ForAll((bb) => { bb.BlockOut = GZipTools.GZipStreamTool.Decompress(bb.Block); });
                                       


                                    foreach (GZipBlock bi in _bbd)
                                    {
                                        _bbw.Write(bi.BlockOut);
                                    }

                                  

                                    _bbd.Clear();
                                    decompressioncounter = 0;
                                }
                                decompressioncounter = decompressioncounter + 1;
                            }

                            if (decompressioncounter > 0)
                            {
                                _bbd.AsParallel().ForAll((bb) => { bb.BlockOut = GZipTools.GZipStreamTool.Decompress(bb.Block); });

                                foreach (GZipBlock bi in _bbd)
                                {
                                    _bbw.Write(bi.BlockOut);
                                }

                            }
                            _bbd.Clear();

                            OnFileActionCompleted();
                            OnFileReadOutCompleted();
                            OnFileSavingCompleted();
                            #endregion

                            break;

                        default: 

                            #region [ Read ]
                            OnFileReadOutBegins();
                            foreach (GZipBlock item in ByteBlockReader.ReagGZipBlock(_bbr.ReaderFileStream, gzbh))
                            {
                                _bbd.Add(item);
                            }
                            OnFileReadOutCompleted();
                            #endregion

                            #region [ Action ] 
                            OnFileActionBegins();
                            /*
                             int counter = 0;
                             foreach (GZipBlock item in _bbd)
                             {
                                 try
                                 {
                                     item.BlockOut =
                                         GZipTools.GZipStreamTool.Decompress(item.Block);
                                 }
                                 catch (Exception e)
                                 {
                                     System.Console.Write(e.Message);
                                 }
                                 counter = counter + 1;
                             }   */


                            _bbd.AsParallel().ForAll((bb) => { bb.BlockOut = GZipTools.GZipStreamTool.Decompress(bb.Block); });
                            OnFileActionCompleted();
                            #endregion

                            #region [ Writeout ]
                            OnFileSavingBegins();
                            using (_bbw.WriterFileStream)
                            {
                                foreach (GZipBlock b in _bbd)
                                {
                                    _bbw.Write(b.BlockOut.ToArray());
                                }
                                _bbd.Clear();
                            }
                            OnFileSavingCompleted();
                            #endregion

                            break;

                    }
                   

                    break;



            }
        }

        #endregion

        #region [ Disposing ]
        /// <summary>
        /// Dispose
        /// </summary>
        public void Dispose()
        {
            _bbr?.Dispose();
            _bbw?.Dispose();

        }
        #endregion

    }
}


